package com.investment.advisor.service.impl;

import com.investment.advisor.dto.FactorDTO;
import com.investment.advisor.dto.StrategyDTO;
import com.investment.advisor.entity.Factor;
import com.investment.advisor.entity.Strategy;
import com.investment.advisor.entity.StrategyFactor;
import com.investment.advisor.repository.FactorRepository;
import com.investment.advisor.repository.StrategyFactorRepository;
import com.investment.advisor.repository.StrategyRepository;
import com.investment.advisor.service.StrategyService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityNotFoundException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class StrategyServiceImpl implements StrategyService {

    private final StrategyRepository strategyRepository;
    private final FactorRepository factorRepository;
    private final StrategyFactorRepository strategyFactorRepository;

    @Autowired
    public StrategyServiceImpl(
            StrategyRepository strategyRepository, 
            FactorRepository factorRepository,
            StrategyFactorRepository strategyFactorRepository) {
        this.strategyRepository = strategyRepository;
        this.factorRepository = factorRepository;
        this.strategyFactorRepository = strategyFactorRepository;
    }

    @Override
    public List<StrategyDTO> getAllStrategies() {
        return strategyRepository.findAll().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    public StrategyDTO getStrategyById(Long id) {
        return strategyRepository.findById(id)
                .map(this::convertToDTO)
                .orElseThrow(() -> new EntityNotFoundException("Strategy not found with id: " + id));
    }

    @Override
    public List<StrategyDTO> searchStrategies(String keyword) {
        return strategyRepository.findByNameContaining(keyword).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    public List<StrategyDTO> getStrategiesByFactorId(Long factorId) {
        return strategyRepository.findByFactorId(factorId).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public StrategyDTO createStrategy(StrategyDTO strategyDTO) {
        Strategy strategy = convertToEntity(strategyDTO);
        Strategy savedStrategy = strategyRepository.save(strategy);
        return convertToDTO(savedStrategy);
    }

    @Override
    @Transactional
    public StrategyDTO updateStrategy(Long id, StrategyDTO strategyDTO) {
        if (!strategyRepository.existsById(id)) {
            throw new EntityNotFoundException("Strategy not found with id: " + id);
        }
        
        Strategy strategy = convertToEntity(strategyDTO);
        strategy.setId(id);
        Strategy updatedStrategy = strategyRepository.save(strategy);
        return convertToDTO(updatedStrategy);
    }

    @Override
    @Transactional
    public void deleteStrategy(Long id) {
        if (!strategyRepository.existsById(id)) {
            throw new EntityNotFoundException("Strategy not found with id: " + id);
        }
        strategyRepository.deleteById(id);
    }

    @Override
    @Transactional
    public void addFactorToStrategy(Long strategyId, Long factorId) {
        Strategy strategy = strategyRepository.findById(strategyId)
                .orElseThrow(() -> new EntityNotFoundException("Strategy not found with id: " + strategyId));
                
        Factor factor = factorRepository.findById(factorId)
                .orElseThrow(() -> new EntityNotFoundException("Factor not found with id: " + factorId));
        
        // 检查是否已存在该因子
        StrategyFactor existingStrategyFactor = strategyFactorRepository.findByStrategyAndFactor(strategy, factor);
        
        if (existingStrategyFactor == null) {
            // 创建新的策略因子关联
            StrategyFactor strategyFactor = new StrategyFactor();
            strategyFactor.setStrategy(strategy);
            strategyFactor.setFactor(factor);
            strategyFactor.setWeight(factor.getWeight() != null ? factor.getWeight() : BigDecimal.ONE); // 默认权重为1或使用因子默认权重
            
            strategyFactorRepository.save(strategyFactor);
        }
    }

    @Override
    @Transactional
    public void removeFactorFromStrategy(Long strategyId, Long factorId) {
        if (!strategyRepository.existsById(strategyId)) {
            throw new EntityNotFoundException("Strategy not found with id: " + strategyId);
        }
        
        if (!factorRepository.existsById(factorId)) {
            throw new EntityNotFoundException("Factor not found with id: " + factorId);
        }
        
        strategyFactorRepository.deleteByStrategyIdAndFactorId(strategyId, factorId);
    }

    @Override
    @Transactional
    public void updateFactorWeight(Long strategyId, Long factorId, BigDecimal weight) {
        Strategy strategy = strategyRepository.findById(strategyId)
                .orElseThrow(() -> new EntityNotFoundException("Strategy not found with id: " + strategyId));
                
        Factor factor = factorRepository.findById(factorId)
                .orElseThrow(() -> new EntityNotFoundException("Factor not found with id: " + factorId));
        
        StrategyFactor strategyFactor = strategyFactorRepository.findByStrategyAndFactor(strategy, factor);
        
        if (strategyFactor == null) {
            throw new EntityNotFoundException("Factor is not in this strategy");
        }
        
        strategyFactor.setWeight(weight);
        strategyFactorRepository.save(strategyFactor);
    }
    
    private StrategyDTO convertToDTO(Strategy strategy) {
        StrategyDTO strategyDTO = new StrategyDTO();
        BeanUtils.copyProperties(strategy, strategyDTO, "strategyFactors");
        
        if (strategy.getStrategyFactors() != null) {
            List<FactorDTO> factorDTOs = new ArrayList<>();
            for (StrategyFactor strategyFactor : strategy.getStrategyFactors()) {
                Factor factor = strategyFactor.getFactor();
                FactorDTO factorDTO = new FactorDTO();
                // 只复制基本属性，避免循环引用
                factorDTO.setId(factor.getId());
                factorDTO.setName(factor.getName());
                factorDTO.setCategory(factor.getCategory());
                factorDTO.setDescription(factor.getDescription());
                factorDTO.setFormula(factor.getFormula());
                factorDTO.setWeight(strategyFactor.getWeight()); // 使用策略因子中的权重
                factorDTO.setCreator(factor.getCreator());
                factorDTO.setCreateTime(factor.getCreateTime());
                factorDTO.setUpdateTime(factor.getUpdateTime());
                factorDTOs.add(factorDTO);
            }
            strategyDTO.setFactors(factorDTOs);
        }
        
        return strategyDTO;
    }
    
    private Strategy convertToEntity(StrategyDTO strategyDTO) {
        Strategy strategy = new Strategy();
        BeanUtils.copyProperties(strategyDTO, strategy, "id", "factors", "createTime", "updateTime");
        
        // 因子关系在保存后通过addFactorToStrategy方法单独处理
        
        return strategy;
    }
} 